package adminservices

import (
	"errors"
	"game-server/app/common/enum"
	"game-server/app/common/request"
	"game-server/app/models"
	"game-server/global"
	"sort"

	"gorm.io/gorm"
)

type sysMenuService struct{}

var SysMenuService = new(sysMenuService)

// GetAllMenus 获取所有菜单 buildMenuTree
func (s *sysMenuService) GetAllMenus() ([]models.SysMenu, error) {
	var menus []models.SysMenu

	// 查询所有启用的菜单，并按照排序字段排序
	err := global.App.DB.Where("status = ?", enum.Enabled).
		Order("sort asc").
		Find(&menus).Error
	if err != nil {
		return nil, err
	}

	// 转换为树形结构
	return buildMenuTree(menus, 0), nil
}

// 构建菜单树
func buildMenuTree(menus []models.SysMenu, parentId uint) []models.SysMenu {
	var tree []models.SysMenu

	for _, menu := range menus {
		if menu.ParentId == parentId {
			menu.Children = buildMenuTree(menus, menu.ID)
			tree = append(tree, menu)
		}
	}

	return tree
}

// 删除菜单
func (s *sysMenuService) DeleteMenu(id string) error {
	var menu models.SysMenu

	// 查询菜单是否存在
	if err := global.App.DB.First(&menu, id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return errors.New("菜单不存在")
		}
		return err
	}

	// 检查是否有子菜单
	var count int64
	if err := global.App.DB.Model(&models.SysMenu{}).Where("parent_id = ?", id).Count(&count).Error; err != nil {
		return err
	}
	if count > 0 {
		return errors.New("存在子菜单，不能删除")
	}
	//method 1
	// 检查菜单是否已被角色使用
	var roleCount int64
	if err := global.App.DB.Model(&models.SysRole{}).
		Joins("JOIN sys_role_menus ON sys_roles.id = sys_role_menus.role_id").
		Where("sys_role_menus.menu_id = ?", id).
		Count(&roleCount).Error; err != nil {
		return err
	}
	if roleCount > 0 {
		return errors.New("菜单已被角色使用，不能删除")
	}
	//method 2
	// 检查菜单是否已被用户使用
	// var userCount int64
	// global.App.DB.Model(&models.SysRoleMenu{}).Where("menu_id =?", id).Count(&userCount)
	// if userCount > 0 {
	// 	return errors.New("菜单已被用户使用，不能删除")
	// }

	// 删除菜单
	return global.App.DB.Delete(&menu).Error
}

// 获取菜单详情
func (s *sysMenuService) GetMenuById(id string) (*models.SysMenu, error) {
	var menu models.SysMenu

	err := global.App.DB.First(&menu, id).Error
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New("菜单不存在")
		}
		return nil, err
	}

	return &menu, nil
}

// 分页条件查询菜单列表
func (s *sysMenuService) GetMenuList(req request.SysMenuListRequest) (list []models.SysMenu, total int64, err error) {
	limit := req.PageSize
	offset := req.PageSize * (req.Page - 1)

	// 构建查询
	db := global.App.DB.Model(&models.SysMenu{})

	// 条件查询
	if req.Name != "" {
		db = db.Where("name LIKE ?", "%"+req.Name+"%")
	}
	if req.Path != "" {
		db = db.Where("path LIKE ?", "%"+req.Path+"%")
	}
	if req.Status != nil {
		db = db.Where("status = ?", *req.Status)
	}
	if req.ParentId != nil {
		db = db.Where("parent_id = ?", *req.ParentId)
	}

	// 查询总数
	err = db.Count(&total).Error
	if err != nil {
		return nil, 0, err
	}

	// 查询数据
	err = db.Limit(limit).Offset(offset).
		Order("sort asc, id desc").
		Find(&list).Error
	if err != nil {
		return nil, 0, err
	}

	return list, total, nil
}

// 创建菜单
func (s *sysMenuService) CreateMenu(req request.SysMenuAddRequest) (*models.SysMenu, error) {
	// 如果父级ID不为0，检查父级菜单是否存在
	if req.ParentId != 0 {
		var parent models.SysMenu
		if err := global.App.DB.First(&parent, req.ParentId).Error; err != nil {
			return nil, errors.New("父级菜单不存在")
		}
	}

	// 检查同级菜单下名称是否重复
	var count int64
	if err := global.App.DB.Model(&models.SysMenu{}).
		Where("parent_id = ? AND name = ?", req.ParentId, req.Name).
		Count(&count).Error; err != nil {
		return nil, err
	}
	if count > 0 {
		return nil, errors.New("同级菜单下已存在相同名称")
	}

	// 创建菜单
	menu := &models.SysMenu{
		ParentId: req.ParentId,
		Name:     req.Name,
		Path:     req.Path,
		Visible:  enum.Enabled,

		Perms:     req.Perms,
		ApiPath:   req.ApiPath,
		Component: req.Component,
		Icon:      req.Icon,
		Sort:      req.Sort,

		Status:  enum.Enabled,
		Hidden:  enum.Disabled,
		Type:    req.Type,
		Remark:  req.Remark,
		IsFrame: req.IsFrame,
	}

	if err := global.App.DB.Create(menu).Error; err != nil {
		return nil, err
	}

	return menu, nil
}

// 更新菜单
func (s *sysMenuService) UpdateMenu(id string, req request.SysMenuUpdateRequest) error {
	var menu models.SysMenu
	// 查询菜单是否存在
	if err := global.App.DB.First(&menu, id).Error; err != nil {
		return errors.New("菜单不存在")
	}

	// 不能将菜单设置为自己的子菜单
	if menu.ID == req.ParentId {
		return errors.New("不能将菜单设置为自己的子菜单")
	}

	// 如果修改了父级菜单，检查新的父级菜单是否存在
	if req.ParentId != menu.ParentId && req.ParentId != 0 {
		var parent models.SysMenu
		if err := global.App.DB.First(&parent, req.ParentId).Error; err != nil {
			return errors.New("父级菜单不存在")
		}

		// 检查是否会形成循环
		if s.isCircularReference(menu.ID, req.ParentId) {
			return errors.New("不能将菜单设置为其子菜单的子菜单")
		}
	}

	// 检查同级菜单下名称是否重复
	var count int64
	if err := global.App.DB.Model(&models.SysMenu{}).
		Where("parent_id = ? AND name = ? AND id != ?", req.ParentId, req.Name, id).
		Count(&count).Error; err != nil {
		return err
	}
	if count > 0 {
		return errors.New("同级菜单下已存在相同名称")
	}

	// 更新菜单信息
	menu.ParentId = req.ParentId
	menu.Name = req.Name
	menu.Path = req.Path
	menu.Visible = req.Visible
	menu.Perms = req.Perms
	menu.ApiPath = req.ApiPath
	menu.Component = req.Component
	menu.Icon = req.Icon
	menu.Sort = req.Sort
	menu.Status = req.Status
	menu.Type = req.Type
	menu.Remark = req.Remark
	menu.IsFrame = req.IsFrame
	menu.Hidden = req.Hidden

	return global.App.DB.Save(&menu).Error
}

// 检查是否会形成循环引用
func (s *sysMenuService) isCircularReference(menuId, newParentId uint) bool {
	var parent models.SysMenu
	for {
		if err := global.App.DB.First(&parent, newParentId).Error; err != nil {
			return false
		}
		if parent.ParentId == 0 {
			return false
		}
		if parent.ParentId == menuId {
			return true
		}
		newParentId = parent.ParentId
	}
}

// 通过角色ID获取菜单
func (s *sysMenuService) GetRoleMenus(roleId string) ([]models.SysMenu, error) {
	var role models.SysRole

	// 查询角色是否存在
	if err := global.App.DB.First(&role, roleId).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New("角色不存在")
		}
		return nil, err
	}

	// 获取角色的菜单权限
	var menus []models.SysMenu
	if err := global.App.DB.Model(&role).
		Association("Menus").
		Find(&menus); err != nil {
		return nil, err
	}

	// 按照排序字段排序
	sort.Slice(menus, func(i, j int) bool {
		return menus[i].Sort < menus[j].Sort
	})

	return menus, nil
}

// 为角色分配菜单
func (s *sysMenuService) AssignMenus(req request.SysMenuAssignRequest) error {
	var role models.SysRole

	// 查询角色是否存在
	if err := global.App.DB.First(&role, req.RoleId).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return errors.New("角色不存在")
		}
		return err
	}

	// 检查是否为超级管理员角色
	if role.Code == "super_admin" {
		return errors.New("不能修改超级管理员的权限")
	}

	// 检查菜单是否都存在且状态正常
	var menus []models.SysMenu
	if err := global.App.DB.Where("id IN ? AND status = ?", req.MenuIds, enum.Enabled).Find(&menus).Error; err != nil {
		return err
	}
	if len(menus) != len(req.MenuIds) {
		return errors.New("部分菜单不存在或已禁用")
	}

	// 使用事务更新角色菜单关联
	return global.App.DB.Transaction(func(tx *gorm.DB) error {
		// 清除原有的菜单关联
		if err := tx.Model(&role).Association("Menus").Clear(); err != nil {
			return err
		}

		// 建立新的菜单关联
		if err := tx.Model(&role).Association("Menus").Replace(menus); err != nil {
			return err
		}

		return nil
	})
}

// GetUserMenus 获取用户的菜单权限
func (s *sysMenuService) GetUserMenus(userId uint) ([]models.SysMenu, error) {
	var user models.SysUser
	if err := global.App.DB.Preload("Roles.Menus", "status = ?", enum.Enabled).
		First(&user, userId).Error; err != nil {
		return nil, err
	}

	// 获取用户所有角色的菜单
	menuMap := make(map[uint]models.SysMenu)
	for _, role := range user.Roles {
		for _, menu := range role.Menus {
			menuMap[menu.ID] = menu
		}
	}
	//menuMap去重
	// 转换为切片并去重排序
	var menus []models.SysMenu
	for _, menu := range menuMap {
		menus = append(menus, menu)
	}
	sort.Slice(menus, func(i, j int) bool {
		return menus[i].Sort < menus[j].Sort
	})

	return buildMenuTree(menus, 0), nil
}
